#include "dw_net.hpp"
#include "test_common.hpp"

#include <chrono>
#include <iostream>

#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
using namespace std;

bool kbhit()
{
    termios term;
    tcgetattr(0, &term);

    termios term2 = term;
    term2.c_lflag &= ~ICANON;
    tcsetattr(0, TCSANOW, &term2);

    int byteswaiting;
    ioctl(0, FIONREAD, &byteswaiting);

    tcsetattr(0, TCSANOW, &term);

    return byteswaiting > 0;
}

void echo_on()
{
    static bool init = false;
    static struct termios init_setting;
    if (!init)
    {
        tcgetattr(0, &init_setting);
        init = true;
    }

    struct termios new_setting = init_setting;

    new_setting.c_lflag |= ECHO;
    tcsetattr(0, TCSANOW, &new_setting);
}

void echo_off()
{
    static bool init = false;
    static struct termios init_setting;
    if (!init)
    {
        tcgetattr(0, &init_setting);
        init = true;
    }

    struct termios new_setting = init_setting;

    new_setting.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &new_setting);
}

uint32_t dw::net::Scramble(uint32_t in)
{
    return in / 2;
}

class CustomClient : public dw::net::client_interface<MsgTypes>
{
public:
    void PingServer()
    {
        dw::net::message<MsgTypes> msg(MsgTypes::ServerPing);

        std::chrono::system_clock::time_point timeNow = std::chrono::system_clock::now();

        msg << timeNow;
        
        Send(msg);
    }

    void MessageAll()
    {
        dw::net::message<MsgTypes> msg(MsgTypes::MessageAll);
        Send(msg);
    }
};

int main()
{
    CustomClient c;
    c.Connect("43.142.137.93", 60000);

    echo_off();
    
    bool bQuit = false;
    while (!bQuit)
    {
        if (kbhit())
        {
            int key = fgetc(stdin);

            switch (key)
            {
            case '1':
                c.PingServer();
                break;
            case '2':
                c.MessageAll();
                break;
            case '3':
                bQuit = true;
                break;
            default:
                break;
            }
        }

        if (c.IsConnected())
        {
            if (!c.Incomming().empty())
            {
                auto msg = c.Incomming().pop_front().msg;

                switch (msg.header.type)
                {
                    case MsgTypes::ServerAccept:
                    {
                        std::cout << "Server Accepted Connection!" << std::endl;
                    }
                    break;
    
                    case MsgTypes::ServerPing:
                    {
                        std::chrono::system_clock::time_point timeNow = std::chrono::system_clock::now();
                        std::chrono::system_clock::time_point timeThen;
                        msg >> timeThen;
                        std::cout << "Ping: " << std::chrono::duration<double>(timeNow - timeThen).count() << std::endl;
                    }
                    break;
    
                    case MsgTypes::ServerMessage:
                    {
                        uint32_t clientID;
                        msg >> clientID;
                        std::cout << "Hello from [" << clientID << "]" << std::endl;
                    }
                    break;
    
                    case MsgTypes::AssignID:
                    {
                        uint32_t clientID;
                        msg >> clientID;
                        clientID = ntohl(clientID);
                        std::cout << "Assigned ID: " << clientID << std::endl;
                    }
                    break;
                }
            }
        }
        else
        {
            std::cout << "Lost Connection to Server!" << std::endl;
            bQuit = true;
        }
    }
    
    echo_on();
    return 0;
}